﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WordNet.Core.ElementContracts;
using WordNet.Core.Relations;

namespace WordNet.Core
{
    public class SenseLexicalTracer:IBothRelations
    {
        IWNDictionary _dictionary;

        public SenseLexicalTracer(IWNDictionary dic)
        {
            _dictionary = dic;
        }
        
        public ISenseLexicalResult Find(IWordID wordId, Pointer pointer)
        {
            if (wordId == null)
                throw new ArgumentNullException();

            if (pointer.Relation != RelationType.Both)
                throw new ArgumentException("it is not correct relation");

            if (!IsAcceptedPOS(pointer, wordId.PartOfSpeech))
                return null;
            
            var result = FindSemantic(wordId, pointer);
            if (result == null)
                result = FindLexical(wordId, pointer);
            return result;
            
        }

        public ISenseLexicalResult Domain(IWordID wordId)
        {
            if (wordId == null)
                throw new ArgumentNullException();

            var resut = Find(wordId, Pointer.REGION);
            if (resut == null)
                resut = Find(wordId, Pointer.TOPIC);
            if (resut == null)
                resut = Find(wordId, Pointer.USAGE);
            return resut;
           
        }

        public ISenseLexicalResult Member(IWordID wordId)
        {
            if (wordId == null)
                throw new ArgumentNullException();

            var result = Find(wordId, Pointer.REGION_MEMBER);
            if (result == null)
                result = Find(wordId, Pointer.TOPIC_MEMBER);
            if (result == null)
                result = Find(wordId, Pointer.USAGE_MEMBER);
            return result;
        }

        private ISenseLexicalResult FindSemantic(IWordID id, Pointer pointer)
        {
            SenseLexicalResult result = null;
            ISynset synset = _dictionary.GetSynset(id.SynsetID);
            List<ISynsetID> relatedSynsets = synset.GetRelatedSynsets(pointer);
            if (relatedSynsets != null)
            {
                List<ISynset> synsets = new List<ISynset>();
                foreach (var synId in relatedSynsets)
                {
                    var relatedSynset = _dictionary.GetSynset(synId);
                    if( relatedSynset != null)
                        synsets.Add(relatedSynset);
                }
                result = new SenseLexicalResult(id, synsets, pointer);
            }
            return result;
        }

        private ISenseLexicalResult FindLexical(IWordID id, Pointer pointer)
        {
            SenseLexicalResult result = null;
            IWord word = _dictionary.GetWord(id);
            List<IWordID> relatedWords = word.GetRelatedWords(pointer);
            if (relatedWords != null)
            {
                List<IWord> words = new List<IWord>();
                foreach (var wordId in relatedWords)
                {
                    var relatedWord = _dictionary.GetWord(wordId);
                    if (relatedWord != null)
                        words.Add(relatedWord);
                }

                result = new SenseLexicalResult(id, words, pointer);
            }
            return result;
        }

        private static bool IsAcceptedPOS(Pointer pointer, POS pos)
        {
            if (pointer == Pointer.TOPIC_MEMBER ||
                pointer == Pointer.USAGE_MEMBER ||
                pointer == Pointer.REGION_MEMBER)
                return POS.NOUN == pos;
            else if (pointer == Pointer.ALSO_SEE)
                return (pos == POS.VERB) || (pos == POS.ADJECTIVE);
            else if (pointer == Pointer.VERB_GROUP)
                return pos == POS.VERB;
            else
                throw new ArgumentException("wrong pointer");
        }
    }
}
